/*
 * Copyright (c) 2024-2025 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <plat/config.h>
#include <core/timer.h>
#include <core/schedule.h>

time_data_t  time_datas[CONFIG_SMP_CORES];
unsigned int cpu_usage = 0;
#define USAGE_SIZE 100
struct usage_queue {
    unsigned long head;
    unsigned long tail;
    char          buf[USAGE_SIZE];
} usage = { 0,
            0,
            {
                0,
            } };

#define FULL(a) ((((a).head + 1UL) % (unsigned long)USAGE_SIZE) == (a).tail)
#define GET(a, i)                              \
    do {                                       \
        (i) = (a).buf[(a).tail];               \
        (a).tail += 1UL;                       \
        (a).tail %= (unsigned long)USAGE_SIZE; \
    } while (0)
#define PUT(a, i)                              \
    do {                                       \
        (a).buf[(a).head] = (i);               \
        (a).head += 1UL;                       \
        (a).head %= (unsigned long)USAGE_SIZE; \
    } while (0)

#define OSMK_TS_MK_INT_IRQ 0

void timer_handler(void)
{
#ifdef DEBUG_DOT_PER_10_INT
    if (++dotcount == 10) {
        dputchar('.'), dotcount = 0;
    }
#endif

    schedule_data_t *sd = get_schedule_data(task_get_coreid(current()));
    if (FULL(usage)) {
        char c;
        GET(usage, c);
        cpu_usage -= (unsigned int)c;
    }
    if (sd->pidle == current()) {
        PUT(usage, 0);
    } else {
        cpu_usage++;
        PUT(usage, 1);
    }

#if OSMK_TS_MK_INT_IRQ
    dprintf("This is timer_handler!\n");
#endif
    time_data_t *time_data = get_time_data(current_cpu());

    time_data->boottime.time += CONFIG_TIMER_TICK_US;

#ifdef CONFIG_KERNEL_MCS
    NODE_STATE_ON_CORE(ksConsumed, current_cpu()) += time_data->countdown;

#ifdef SC_DEBUG
    dprintf("\n\n\ncore:%d,current_time:%u,countdown:%u,ksConsumed:%u,sum_consumed:%u\n", current_cpu(),
            time_data->boottime.time, time_data->countdown, NODE_STATE_ON_CORE(ksConsumed, current_cpu()),
            ksCurSC[current_cpu()]->scConsumed);
    dprintf("checkBudget before:");
    sched_context_dprintf(ksCurSC[current_cpu()]);
#endif    // SC_DEBUG

    checkBudget(current_cpu());

#ifdef SC_DEBUG
    dprintf("checkBudget after:");
    sched_context_dprintf(ksCurSC[current_cpu()]);
#endif    // SC_DEBUG

#endif    // CONFIG_KERNEL_MCS

    pt_ack_int();

    schedule_wakeup(time_data->boottime.time);

    tcb_t *cur = current();
    if (cur->sched_policy == SCHED_RR) {
        schedule_exec_rr(cur);
    } else if (cur->sched_policy == SCHED_SPORADIC) {
        schedule_exec_sched_sporadic(cur);
    }

    schedule_exec_repl(sd);

#if CONFIG_DOM_NUMBER > 1
    deductCurDomainTime(time_data->countdown);

    if (getCurDomainTime() <= 0) {
        changeToNextDomain();
    }
#endif

    schedule();

    timer_next(CONFIG_TIMER_TICK_US);
}

void timer_next(unsigned int us)
{
    time_data_t *time_data = get_time_data(current_cpu());
    (void)pt_set_next(us);

    time_data->countdown = us;
}

void timer_datas_init(unsigned int core)
{
    (void)core;
    int i;
    for (i = 0; i < (int)(sizeof(time_datas) / sizeof(time_data_t)); i++) {
        time_datas[i].boottime.time = 0x00000;
        time_datas[i].countdown     = 0;
    }
}
